package com.lefevre.cms.utils;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;

/**
 * 文件权限
 */
public class FileAuthorizationDetection {
    private static final Logger logger = LoggerFactory.getLogger(FileAuthorizationDetection.class);

    /**
     * 检测文件权限
     *
     * @return (返回不可读或不可写的绝对路径)
     */
    public static String detection() {
        final String[] info = {null};//如果需要内部类修改局部变量，并且能传到外部类。因为final只是不能改变它的指向,但是可以改变它的属性,所以可以用数组来代替
        //检测路径
        String path = PathUtil.defaultExternalDirectory() + File.separator;
        //排除目录路径
        String excludeDirectoryPath = PathUtil.defaultExternalDirectory() + File.separator + "file" + File.separator;
        try {
            Files.walkFileTree(Paths.get(path), new SimpleFileVisitor<Path>() {
                //访问子目录之前触发该方法
                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    //排除路径
                    if (StringUtils.startsWithIgnoreCase(dir.toString(), excludeDirectoryPath)) {
                        return FileVisitResult.SKIP_SIBLINGS;//代表“继续访问”，但不访问该文件或目录的兄弟文件或目录
                    }
                    return FileVisitResult.CONTINUE;//代表“继续访问”的后续行为
                }

                //访问子目录之后触发该方法
                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    if (!Files.isHidden(dir)) {//如果不是隐藏文件
                        //检测文件夹权限
                        if (!Files.isReadable(dir) || !Files.isWritable(dir)) {
                            //Files.isWritable判断文件夹是否可写方法不可靠，对于匿名(只读)访问一个网络共享文件夹,isWritable返回是true (匿名用户只有读取权限的共享文件夹Files.isWritable(...)总是返回true)
                            info[0] = dir + (!Files.isReadable(dir) ? " [目录不可读]" : "") + (!Files.isWritable(dir) ? " [目录不可写]" : "");
                            return FileVisitResult.TERMINATE;//代表“终止访问”的后续行为
                        }
                    }
                    return FileVisitResult.CONTINUE;//代表“继续访问”的后续行为
                }

                //访问文件时触发该方法
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    if (!Files.isHidden(file)) {//如果不是隐藏文件
                        //检测文件夹权限
                        if (!Files.isReadable(file) || !Files.isWritable(file)) {
                            info[0] = file + (!Files.isReadable(file) ? " [文件不可读]" : "") + (!Files.isWritable(file) ? " [文件不可写]" : "");
                            return FileVisitResult.TERMINATE;//代表“终止访问”的后续行为
                        }
                    }
                    return FileVisitResult.CONTINUE;//代表“继续访问”的后续行为
                }

                //访问文件失败时触发该方法
                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException e) {
            logger.error("检测文件权限错误", e);
        }
        return info[0];
    }
}
